home *** CD-ROM | disk | FTP | other *** search
Korn shell script | 1997-08-26 | 19.3 KB | 567 lines |
- #!/bin/ksh
- # @(#) brandimg.ksh 1.2 96/11/27
- # 94/04/17 john h. dubois iii (john@armory.com)
- # 94/12/13 ksh compatibility fix
- # 95/01/03 More fixes for pd-ksh compatibility
- # 96/01/19 Added T option.
- # 96/11/27 Allow other input formats than pnm. Added GE extension.
-
- # Replacement for print, because pd-ksh(?) doesn't have -u option.
- alias print=uprint
- function uprint {
- if [ "$1" = -u2 ]; then
- shift # get rid of -u2
- \print "$@" >&2
- else
- \print "$@"
- fi
- }
-
- # Usage: pnminfo File
- # Puts the width of File in InfX, and the height in InfY
- # Changed to use command line substitution because pd-ksh creates a
- # separate shell for pipes, so pipe-into-read doesn't work the way we want.
- typeset -i InfX InfY
- function pnminfo {
- # typeset j1 j2 j3 j4
-
- if [ $# -gt 0 ]; then
- # pnmfile "$1" | read j1 type j2 InfX j3 InfY j4
- set -- $(pnmfile "$1")
- InfX=$4 InfY=$6
- else
- # pnmfile | read j1 type j2 InfX j3 InfY
- set -- $(pnmfile)
- InfX=$4 InfY=$6
- fi
- }
-
- # Finds the width and height of the piece to cut out of the image for branding,
- # and stores them in globals Width and Height.
- # Also sets ExtractProc and InsertProc to pipelines that should be used in
- # extracting material from image and inserting modified material back into
- # image, if it is neccessary to flip the material or truncate the brand.
- # Usage: FindCutParms filename brand-width brand-height
- # filename is the image file to cut from.
- # brand-width and brand-height are the dimensions of the branding files.
- # Other global vars used: Debug
- typeset -i Width Height
- function FindCutParms {
- typeset File=$1 Flip=false
- typeset -i BrandWidth=$2 BrandHeight=$3 tm
-
- ExtractProc=
- InsertProc=
- Width=BrandWidth
- Height=BrandHeight
- pnminfo $File
- $Debug && print -u2 "Image $File: width=$InfX height=$InfY"
- # -le is used instead of -lt because image must be one pixel larger
- # than brand due to bug in some pnm utilities
- if [ InfX -le BrandWidth ]; then
- if [ InfY -gt BrandWidth ]; then
- Flip=true
- else
- $Debug && print -u2 "Brand won't fit; truncating."
- let Width=Width-1
- ExtractProc="|Expand 0 $((BrandWidth-Width)) 0 0"
- InsertProc="pnmcut 0 0 $Width $Height|"
- print -u2 \
- "Truncated brand for $File to $(((100*Width)/BrandWidth))% of original length."
- [ InfX -lt InfY ] && Flip=true
- fi
- fi
- if $Flip; then
- tm=Width
- Width=Height
- Height=tm
- ExtractProc="|pnmflip -ccw $ExtractProc"
- InsertProc="$InsertProc pnmflip -cw|"
- $Debug && print -u2 "Inserting brand vertically."
- else
- $Debug && print -u2 "Inserting brand horizontally."
- fi
- $Debug && print -u2 \
- "Addtl extraction pipe: $ExtractProc\nAddtl insertion pipe: $InsertProc"
- return 0
- }
-
- # Usage: Invert conversion-type source-material mask
- # Operations are performed on the source material to make it suitable for
- # branding.
- # Material is then cut out using mask and sent to the standard output.
- # Conversion is one of [iIbBmM]
- # iI Invert only bB Quantize to 8 colors mM Render in black/white
- # IBM Convolve first
- # Source is inverted, then convolved if required,
- # then quantized or reduced to black/white if required.
- # Global vars used: ConvolTmp, QuantTmp
- function Invert {
- typeset Conversion=$1 Source=$2 Mask=$3 Cmd=
-
- if [[ "$Conversion" = [IBM] ]]; then
- # This convolution replaces each pixel with the average of its
- # neighbors, with no weight given to the pixel itself.
- # Note that the edges pixels won't be convolved since it's only
- # done on pixels that have neighbors all around.
- Cmd="|pnmconvol $ConvolTmp"
- [ ! -f $ConvolTmp ] && echo \
- "P2
- 3 3
- 16
- 9 9 9
- 9 8 9
- 9 9 9" > $ConvolTmp
- fi
-
- case "$Conversion" in
- [bB]) Cmd="$Cmd | ppmquant -map $QuantTmp"
- # This will push colors to 0 or full value
- [ ! -f $QuantTmp ] && echo \
- "P3
- 8 1
- 255
- 0 0 0
- 255 0 0
- 0 255 0
- 0 0 255
- 255 255 0
- 255 0 255
- 0 255 255
- 255 255 255" > $QuantTmp
- ;;
- [mM])
- Cmd="$Cmd | ppmtopgm | pgmtopbm -threshold -value 0.5"
- ;;
- esac
- # Convert source to the form that we want to use,
- # and then cut what we want out of it
- $Debug && print -u2 \
- "Inversion type: $Conversion\nInversion pipe extra commands: $Cmd
- Inversion source file: $Source\nInversion mask: $Mask"
- eval pnminvert $Source $Cmd | pnmarith -mult $Mask -
- return 0
- }
-
- # Usage: MakeText "text" output-file border-width [fontfile]
- # Makes pbm text, using fontfile if given, and puts output in output-file.
- # The text is cropped to remove margins.
- # If border-width is nonzero, a margin of the given thickness is left.
- # Sets globals TextWidth and TextHeight to width and height of output file.
- # Other global variables: CroppedTmp, Debug
- function MakeText {
- typeset Text=$1 OutputFile=$2 FontOpt FontFile TmpOutput
- typeset -i BorderWidth=$3
-
- if [ $# -eq 4 ]; then
- FontFile=$4
- $Debug && print -u2 "Font file is $FontFile"
- if [ ! -r "$FontFile" ]; then
- print -u2 "Cannot read font file $FontFile. Exiting."
- exit 1
- fi
- FontOpt="-font $FontFile"
- fi
- [ $BorderWidth -eq 0 ] && TmpOutput=$OutputFile || TmpOutput=$CroppedTmp
- # Fonts are black on white.
- # Crop white off, then invert to give white on black.
- pbmtext $FontOpt "$Text" | pnmcrop -white | pnminvert > $TmpOutput
- pnminfo $TmpOutput
- if [ $BorderWidth -ne 0 ]; then
- # Add margin around text.
- TextWidth="InfX+(2*BorderWidth)"
- TextHeight="InfY+(2*BorderWidth)"
- pbmmake -black $TextWidth $TextHeight |
- pnmpaste -replace $TmpOutput $BorderWidth $BorderWidth > $OutputFile
- else
- TextWidth=InfX
- TextHeight=InfY
- fi
- $Debug && print -u2 "Text width=$TextWidth; height=$TextHeight"
- return 0
- }
-
- # Usage:
- # MakeMasks text width height border-width text-color allmask-file bmask-file
- # Make a white-on-black masks for the border and for all of the fixed material
- # (the text mask is the text itself).
- # Textfile should be white on black. Its size is given by width & height.
- # Border mask is written to bmask-file.
- # All-fixed-mask is written to allmask-file.
- # Text is grown by border-width (1 or more) pixels. The grown part is the
- # border mask; the text and grown part is the all-mask.
- # If text-color is n (indicating that only the border will be masked into the
- # image), the grown part only is used for the all-mask.
- # Global vars used: FullMaskInTmp, FullMaskOutTmp
- function MakeMasks {
- typeset GrowConv tm TextFile=$1 Cmd= FullTee= BOnlyTee=
- typeset -i TextWidth=$2 TextHeight=$3 BorderWidth=$4
- typeset TextColor=$5 AllMaskFile=$6 BorderMaskFile=$7
-
- GrowConv="P2
- 3 3
- 2
- 2 2 2
- 2 2 2
- 2 2 2"
- # First, make a mask that is one pixel larger, because pnmconvol won't
- # operate on the outermost pixels given a 3x3 convolution matrix
- pbmmake -black $((TextWidth+2)) $((TextHeight+2)) |
- pnmpaste -replace $TextFile 1 1 > $FullMaskInTmp
- # Run as many iterations as neccessary.
- # Use pnmconv & pgmtopbm instead of using pbmmask because pbmmask will
- # decide the text is the background if it gets too fat. Also, pbmmask
- # fills in enclosed areas, which we don't want.
- while [ BorderWidth -gt 1 ]; do
- echo "$GrowConv" | pnmconvol - $FullMaskInTmp |
- pgmtopbm -threshold -value 0.1 > $FullMaskOutTmp
- # Swap names of input & output
- tm=$FullMaskOutTmp
- FullMaskOutTmp=$FullMaskInTmp
- FullMaskInTmp=$tm
- let BorderWidth-=1
- done
-
- # Insert the tee that writes the all-mask-file either before or after
- # the text is XORed out.
- [ "$TextColor" = n ] && BOnlyTee="| tee $AllMaskFile" ||
- FullTee="| tee $AllMaskFile"
-
- $Debug && print -u2 "No-text-cmd: $Cmd"
- # Do last grow step.
- # Cut off the temporary margin that was added.
- # If image will be left where original text is, subtract it from mask.
- echo "$GrowConv" | pnmconvol - $FullMaskInTmp |
- pgmtopbm -threshold -value 0.1 |
- eval pnmcut 1 1 $TextWidth $TextHeight - $FullTee |
- eval pnmpaste -xor $TextFile 0 0 - $BOnlyTee > $BorderMaskFile
- }
-
- # Make fixed-color text and/or border & write it to stdout
- # Usage: MakeFixed text-color border-color text-mask border-mask
- # text-mask and border-mask are white-on-black masks to colorize.
- # Globals used: BorderTmp, Debug
- function MakeFixed {
- typeset TextColor=$1 BorderColor=$2 TextMask=$3 BorderMask=$4
- typeset RealBorder RealText BorderCmd
-
- # Make text/border black if they won't included or if their color will
- # be derived from image material
- [[ "$BorderColor" = [niIbBmM] ]] && RealBorder=black ||
- RealBorder=$BorderColor
- [[ "$TextColor" = [niIbBmM] ]] && RealText=black || RealText=$TextColor
- $Debug && print -u2 "Real colors: border=$RealBorder, text=$RealText"
-
- if [ $RealBorder = black ]; then
- # BorderMask will not exist if no border
- pgmtoppm $RealText $TextMask
- else
- pgmtoppm $RealBorder $BorderMask > $BorderTmp
- $Debug && print -u2 "Adding border and text."
- pgmtoppm $RealText $TextMask | pnmarith -add $BorderTmp -
- fi
- return 0
- }
-
- # Usage: GetUnCom filename.ext
- # Sets global getUnCom_ret to the name of an uncompressor appropriate for the
- # given file, based on its extension. If no appropriate uncompressor is known,
- # getUnCom_ret is set to a null string.
- function getUnCom {
- case "${1##*.}" in
- [jJ][pP]?([eE])[gG])
- getUnCom_ret=djpeg
- getUnCom_un=cjpeg
- ;;
- [gG][iI][fF])
- getUnCom_ret=giftopnm
- getUnCom_un=ppmtogif
- ;;
- [pP][bBgGpPnN][mM])
- getUnCom_ret=cat
- getUnCom_un=cat
- ;;
- *)
- getUnCom_ret=
- getUnCom_un=
- ;;
- esac
- }
-
- # Usage: topnm filename [command [args]]
- # A converter to pnm format is chosen based on the filename extension.
- # If a command is given, the output of the converter is piped into it;
- # otherwise it is sent to the standard output.
- # As an optimization, if the file is already in pnm format and a command is
- # given, it is passed as the final argument to command. Therefore, a command
- # should only be given if it is appropriate to pass it a pnm file after all
- # of its fixed arguments.
- # The global DEF_EXT may provide a default for files with unknown extensions.
- # The global name should be set to the program name.
- function topnm {
- typeset uncom= ext filename=$1
- shift
-
- getUnCom "$filename"
- [ -z "$getUnCom_ret" -a -n "$DEF_EXT" ] && getUnCom "$DEF_EXT"
- if [ -z "$getUnCom_ret" ]; then
- print -u2 -- "$name: Do not know how to convert file: $filename"
- return 1
- fi
- if [ $# -gt 0 ]; then
- [ "$getUnCom_ret" = cat ] && "$@" "$filename" ||
- "$getUnCom_ret" < "$filename" | "$@"
- else
- "$getUnCom_ret" < "$filename"
- fi
- return $?
- }
-
- name=${0##*/}
- Usage=\
- "Usage: $name [-hoPx] [-b<text-border-type>] [-t<text-type>] [-e<extension>]
- [-w<border-width>] [-f<pbm-font-file>] [-T<tmp-prefix>] \"text\"
- image-file ..."
-
- BorderColor=blue TextColor=white Extension=.B
- Debug=false Preserve=false Stdout=false
- typeset -i BorderWidth=1
- Tmp=
- progress=false
- DEF_EXT=
-
- while getopts :e:f:hb:t:w:oxPT:E:G opt; do
- case $opt in
- h)
- echo \
- "$name: imprint text on an image.
- $name masks the given text into the bottom of an image, right adjusted and
- surrounded by a one-pixel-wide border to ensure that the text can be seen
- regardless of the color of the area it is inserted into. The border is not a
- box; it flows around the text itself.
- If the text is too long to fit, it is put along the right of the image, bottom
- adjusted. If it is still too long to fit, it is put along the longer dimension
- with the right of the text truncated and a warning is printed.
- Output is written to a file with name of the source file with .B appended, and
- is in the same format as the input file. The file type is derived from the
- filename extension. $name knows how to deal with jpeg, gif, and pbm/pgm/ppm
- files.
- $Usage
- Options:
- -h: Print this help.
- -b, -t: These options specify alternate material to use for the border and text
- respectively. The default is $TextColor text with a $BorderColor border.
- A value of 'n' turns off that component. -bn removes the border; -tn makes
- the image show through inside the border. If either is given and the
- material inserted is the same color as the insertion area, it won't show
- up. A value of 'i' makes that component consist of the image material
- with red, green, and blue each inverted separately. The closer the
- insertion area is to 50% intensity gray, the less the text will show up.
- A value of 'b' is like i, except that the results of the inversion are
- thresholded such that each color has either 0 or full intensity.
- A value of 'm' is like b, except that only black and white are used.
- I, B, and M are the like i, b, and m except that the image material that
- will be used for the text is first run through a convolution such that each
- pixel is replaced with an average of its neighbors, with 0 weight given to
- the pixel itself, so that the color of the text will be derived from the
- pixels that will be next to it rather than the pixels that are replaced.
- If any other value is given with -b or -t, it is taken to be a color to set
- that part to. See the pgmtoppm man page for a description of how colors
- may be specified.
- -o: Write output to standard output. Only one filename should be given.
- -x: Turn on debugging.
- -P: Preserve (do not remove) tmp files.
- -G: Print a progress indication to the standard error output. As each file is
- about to be processed, its name is printed.
- -T: Set the tmp file prefix. By default, tempfiles are placed in the invoking
- user's home directory (or the directory specified by the TMP environment
- variable, if set), and begin with '#b'. If a prefix is given with -T,
- tempfile names are prefixed with the prefix. If the prefix does not
- include a directory component, tempfiles are placed in the current working
- directory.
- -e: Write output to files with the given extension appended, instead of \".B\".
- -E: The given extension specifies a default type for files that have no
- extension or whose extension is not recognized. The extension given should
- be one of gif, jpg, pbm, pgm, or ppm.
- -w: Set the border width (thickness). The default is 1 pixel.
- -f: Use a font other than the default. The font file should be created by
- the means described in the pbmtext man page."
- exit 0
- ;;
- b) BorderColor=$OPTARG
- [ "$BorderColor" = n ] && BorderWidth=0
- ;;
- t) TextColor=$OPTARG
- ;;
- T) Tmp=$OPTARG;;
- o) Stdout=true; unset OutFile;;
- x) Debug=true;;
- G) progress=true;;
- P) Preserve=true;;
- e) Extension=$OPTARG;;
- E) DEF_EXT=$OPTARG
- getUnCom "$DEF_EXT"
- if [ -z "$getUnCom_ret" ]; then
- print -u2 "$name: Unrecognized extension given with -E: $DEF_EXT"
- exit 1
- fi
- ;;
- w) BorderWidth=$OPTARG;;
- f) FontFile=$OPTARG;;
- +?)
- print -u2 "$name: options should not be preceded by a '+'."
- exit 1
- ;;
- :)
- print -u2 "$name: Option '$OPTARG' requires a value. Use -h for help."
- exit 1
- ;;
- ?)
- print -u2 "$name: $OPTARG: bad option. Use -h for help."
- exit 1
- ;;
- esac
- done
-
- if [ "$BorderColor" = n -a "$TextColor" = n ]; then
- print -u2 "No border or text to be inserted. Aborting."
- exit 1
- fi
-
- # remove args that were options
- let OPTIND=OPTIND-1
- shift $OPTIND
-
- # Need at least text and one image file
- if [ $# -lt 2 ]; then
- print -u2 "$Usage\nUse -h for help."
- exit
- fi
-
- # Quit if anything goes wrong
- set -e
-
- Text=$1
- shift
-
- : ${Tmp:=${TMP:-$HOME}/#b}
- BorderTmp=$Tmp.fixbdr.$$ # Fixed border material
- ImageBdrTmp=$Tmp.imgbdr.$$ # Image-derived border material
- BorderMaskTmp=$Tmp.brdmsk.$$ # Mask for border material
- ConvolTmp=$Tmp.imgcnv.$$ # Image convolution matrix
- CroppedTmp=$Tmp.rawtxt.$$ # pbm text, after cropping & before expansion
- TextMaskTmp=$Tmp.exptxt.$$ # White-on-black expanded text
- FullMaskInTmp=$Tmp.bdr-in.$$ # Used in generating border
- FullMaskOutTmp=$Tmp.bdrout.$$ # Used in generating border
- ImagePieceTmp=$Tmp.img-pc.$$ # Text-size piece cut from image
- ImgMaskTmp=$Tmp.imgmsk.$$ # Mask used to cut space in image for brand
- AllBrandTmp=$Tmp.albrnd.$$ # Fixed and image-derived brand to add to image
- FixedMtlTmp=$Tmp.fxbrnd.$$ # Fixed branding material (text and/or border)
- QuantTmp=$Tmp.quantz.$$ # Quantization colors
- ConvertTmp=$Tmp.conv.$$ # Image converted to pnm
-
- $Debug && print -u2 "tmpfile prefix is $Tmp"
-
- $Preserve ||
- trap "rm -f $FixedMtlTmp $ImgMaskTmp $BorderTmp $TextMaskTmp $CroppedTmp "\
- "$FullMaskInTmp $FullMaskOutTmp $ImagePieceTmp $AllBrandTmp $QuantTmp "\
- "$ConvolTmp $BorderMaskTmp $ImageBdrTmp $ConvertTmp" EXIT 1 2 3 15
-
- typeset -i TextWidth TextHeight
-
- # Make raw text & put in $TextMaskTmp
- MakeText "$Text" $TextMaskTmp $BorderWidth $FontFile
-
- # BorderMaskTmp is set up only if there is a border
- if [ $BorderWidth -gt 0 ]; then
- # Make mask for fixed material & put it in $ImgMaskTmp
- MakeMasks $TextMaskTmp $TextWidth $TextHeight $BorderWidth $TextColor \
- $ImgMaskTmp $BorderMaskTmp
- else
- ImgMaskTmp=$TextMaskTmp
- fi
-
- # Put the fixed-color part of the material to be masked in in $FixedMtlTmp.
- MakeFixed $TextColor $BorderColor $TextMaskTmp $BorderMaskTmp > $FixedMtlTmp
-
- # FixedMtlTmp is the fixed-color material.
- # ImgMaskTmp is the mask, white where image should be removed.
- # TextMaskTmp is the white-on-black text bitmap.
- # BorderTmp & CroppedTmp vars are no longer used but may refer to the same
- # files as the other vars.
-
- # Put 'do' on separate line, for pd-ksh
- for infile
- do
- $progress && print -u2 -- "$infile"
- if [ ! -f "$infile" -o ! -r "$infile" ]; then
- print -u2 -- "$name: Cannot read file: $infile"
- exit 1
- fi
- getUnCom "$infile"
- case "$getUnCom_ret" in
- cat)
- file=$infile
- unConvert=
- ;;
- "")
- print -u2 -- "$name: Do not know how to convert file: $infile"
- continue
- ;;
- *)
- topnm "$infile" > $ConvertTmp || {
- print -u2 -- \
- "$name: Conversion to pnm format failed for file: $infile"
- continue
- }
- file=$ConvertTmp
- unConvert="| $getUnCom_un"
- ;;
- esac
- # Set Width & Height to dimensions of piece to cut out, etc.
- FindCutParms $file $TextWidth $TextHeight
-
- # Do not rely in existance of /dev/stdout et al
- $Stdout || {
- if [ "$infile" -ef $infile$Extension ]; then
- print -u2 "Input and output are the same file."
- continue
- fi
- OutFile="> '$infile$Extension'"
- }
- $Debug && print -u2 "Stdout=$Stdout. Output is $OutFile"
-
- # Get image piece
- eval pnmcut -$Width -$Height $Width $Height '"$file"' $ExtractProc \
- > $ImagePieceTmp
-
- # Make image-derived brand components, if neccessary
- if [[ "$TextColor" = [iIbBmM] || "$BorderColor" = [iIbBmM] ]]; then
- # Don't bother adding fixed color material if it's just black
- if [[ "$TextColor" != [niIbBmM] || "$BorderColor" != [niIbBmM] ]]; then
- FixedCmd="| pnmarith -add - $FixedMtlTmp" # Add fixed part of brand
- $Debug && print -u2 "Fixed material add command: $FixedCmd"
- else
- $Debug && print -u2 "No fixed material to add."
- fi
- TextInvCmd="Invert $TextColor $ImagePieceTmp $TextMaskTmp"
- BorderInvCmd="Invert $BorderColor $ImagePieceTmp $BorderMaskTmp"
- if [[ "$TextColor" != [iIbBmM] ]]; then # Only border is image derived
- eval $BorderInvCmd $FixedCmd
- elif [[ "$BorderColor" != [iIbBmM] ]]; then # Only text image derived
- eval $TextInvCmd $FixedCmd
- else # Both are image derived
- eval $BorderInvCmd > $ImageBdrTmp
- eval $TextInvCmd $FixedCmd | pnmarith -add - $ImageBdrTmp
- fi > $AllBrandTmp
- else
- AllBrandTmp=$FixedMtlTmp
- fi
-
- # Put it all back together
- pnmarith -sub $ImagePieceTmp $ImgMaskTmp | # Cut mask out of image
- pnmarith -add - $AllBrandTmp | # Add brand
- eval $InsertProc pnmpaste -replace - -$Width -$Height '"$file"' \
- $unConvert $OutFile
- done
-